import { getCoordinateSystemType } from '@/utils/coordinateSystem'

/**
 * @description 根据WMTS服务的能力文档信息获取关键参数
 * @param {string} wmtsURL WMTS服务的能力文档信息
 * @returns {Promise<Object>} 包含WMTS服务的关键参数的Promise对象
 * @remarks resolveLayers方法返回WMTS服务的地图与具体的矩阵集合信息
 */
export default class WMTS {
  #wmtsInfoDoc = null
  #provider = ''
  #getTileURL = ''
  #DPI = 90.7142857142857 // WMTS DPI默认设置为90.7142857142857
  #mapLayer = []

  constructor(wmtsInfo) {
    this.#wmtsInfoDoc = new DOMParser().parseFromString(wmtsInfo, 'text/xml')
    this.#provider = this.#wmtsInfoDoc.getElementsByTagName('ows:ProviderName')[0] ? this.#wmtsInfoDoc.getElementsByTagName('ows:ProviderName')[0].innerHTML : 'other'
    this.#getTileURL = this.#extractGetTileURL()
  }

  get info() {
    return {
      provider: this.#provider,
      mapLayer: this.#mapLayer,
      getTileURL: this.#getTileURL
    }
  }

  /**
   * @description 解析WMTS服务的GetTileURL地址
   * @returns WMTS GetTileURL 地址
   */
  #extractGetTileURL() {
    const getTileNode = Array.from(this.#wmtsInfoDoc.getElementsByTagName('ows:Operation')).find((node) => node.getAttribute('name') === 'GetTile')
    if (getTileNode) {
      const node = Array.from(getTileNode.getElementsByTagName('ows:Get')).find((n) => n.getElementsByTagName('ows:Value')[0].innerHTML === 'KVP')
      return node ? node.getAttribute('xlink:href').split('?')[0] : null
    }
  }

  /**
   * @description 解析WMTS服务的地图与具体的矩阵集合信息
   * @returns {Promise<Array>} 包含WMTS服务的地图与具体的矩阵集合信息的Promise对象
   */
  async resolveLayers() {
    const layerNodes = this.#wmtsInfoDoc.querySelectorAll('Layer')
    if (layerNodes.length === 0) return
    return await Promise.all(
      Array.from(layerNodes).map(async (layer) => {
        const title = layer.getElementsByTagName('ows:Title')[0]?.innerHTML || ''
        const identifier = layer.getElementsByTagName('ows:Identifier')[0]?.innerHTML || ''
        const boundingBoxes = []
        const tileMatrixSets = []
        for (let j = 2; j <= 4; j++) {
          const bbox = extractBoundingBox(layer.children[j], this.#provider)
          if (bbox) boundingBoxes.push(bbox)
        }
        for (const tileMatrixSet of layer.querySelectorAll('TileMatrixSet')) {
          const tileMatrixSetNode = findTileMatrixSet(this.#wmtsInfoDoc, tileMatrixSet.textContent)
          this.#DPI = tileMatrixSetNode.querySelector('WellKnownScaleSet')?.innerHTML === 'ChinaPublicServices' ? 96 : 90.7142857142857
          const tileMatrixSetInfo = await extractTileMatrixSet(tileMatrixSetNode, this.#DPI)
          tileMatrixSets.push({
            TileMatrixSetName: tileMatrixSet.textContent,
            SupportedCRS: urnToEPSG(tileMatrixSetNode.getElementsByTagName('ows:SupportedCRS')[0]?.innerHTML) || '',
            DPI: this.#DPI,
            WellKnownScaleSet: tileMatrixSetNode.querySelector('WellKnownScaleSet')?.innerHTML || '',
            TileMatrix: tileMatrixSetInfo
          })
        }
        return {
          Title: title,
          Identifier: identifier,
          BoundingBox: boundingBoxes,
          TileMatrixSet: tileMatrixSets
        }
      })
    )
  }
}

/**
 * @description 将 urn 格式的坐标系表述转换为 EPSG Code格式的坐标系表述
 * @param {String} urn urn 格式的坐标系表述
 * @returns EPSG Code格式的坐标系表述
 */
function urnToEPSG(urn) {
  const epsgMatch = urn.match(/urn:ogc:def:crs:EPSG::(\d+)/)
  if (epsgMatch) return 'EPSG:' + epsgMatch[1]
  return urn.match(/urn:ogc:def:crs:OGC:2:(\d+)/) ? 'EPSG:4326' : `EPSG:${urn.split(':').slice(-1)[0]}`
}

/**
 * @description 解析WMTS服务的BoundingBox信息
 * @param {HTMLDOM} childNode WMTS 能力文档的Layers节点
 * @param {String} provider WMTS 服务提供者名称
 * @returns WMTS 服务各个图层与矩阵集合的BoundingBox信息
 */
function extractBoundingBox(childNode, provider) {
  if (!childNode.attributes.crs) return null
  const crs = urnToEPSG(childNode.attributes.crs.nodeValue)
  const lowerCorner = String(childNode.getElementsByTagName('ows:LowerCorner')[0].innerHTML).split(' ')
  const upperCorner = String(childNode.getElementsByTagName('ows:UpperCorner')[0].innerHTML).split(' ')
  let minx, miny, maxx, maxy
  // iServer 发布的WMTS服务的BoundingBox是：非 4326/3857 的坐标系是经度在前纬度在后(x在前y在后)，而其他厂商例如arcserver则是纬度在前经度在后(y在前x在后)
  // 处理办法：单独对 iServer 发布的服务进行判断，做特殊处理
  if (provider === '北京超图软件股份有限公司' || crs === 'EPSG:4326' || crs === 'EPSG:3857') {
    minx = lowerCorner[0]
    miny = lowerCorner[1]
    maxx = upperCorner[0]
    maxy = upperCorner[1]
  } else {
    minx = lowerCorner[1]
    miny = lowerCorner[0]
    maxx = upperCorner[1]
    maxy = upperCorner[0]
  }
  return {
    crs: crs,
    minx: minx,
    miny: miny,
    maxx: maxx,
    maxy: maxy
  }
}

/**
 * @description 根据矩阵集名称获取矩阵集DOM 节点
 * @param {HTMLDOM} xmlDoc WMTS 能力文档 DOM 对象
 * @param {String} tileMatrixSetName WMTS 矩阵集名称
 * @returns WMTS 矩阵集 DOM 节点
 */
function findTileMatrixSet(xmlDoc, tileMatrixSetName) {
  const tileMatrixSets = xmlDoc.getElementsByTagName('TileMatrixSet')
  for (let i = 0; i < tileMatrixSets.length; i++) {
    const tileMatrixSet = tileMatrixSets[i]
    const identifierElements = tileMatrixSets[i].getElementsByTagNameNS('*', 'Identifier')
    if (identifierElements.length > 0 && identifierElements[0].textContent === tileMatrixSetName) return tileMatrixSet
  }
}

/**
 * @description 解析WMTS服务的矩阵集信息
 * @param {HTMLDOM} tileMatrixSetNode WMTS 矩阵集DOM节点
 * @param {*} dpi WMTS DPI
 * @returns WMTS 矩阵集信息
 */
async function extractTileMatrixSet(tileMatrixSetNode, dpi) {
  const tileMatrixSetInfo = {
    Identifier: [],
    ScaleDenominator: [],
    Resolution: [],
    TopLeftCorner: [],
    MatrixWidth: [],
    MatrixHeight: []
  }
  const crsType = await getCoordinateSystemType(urnToEPSG(tileMatrixSetNode.getElementsByTagName('ows:SupportedCRS')[0]?.innerHTML))
  tileMatrixSetNode.querySelectorAll('TileMatrix').forEach((tileMatrixNode) => {
    const [lat, lon] = tileMatrixNode.querySelector('TopLeftCorner').innerHTML.split(' ')
    tileMatrixSetInfo.Identifier.push(Number(tileMatrixNode.getElementsByTagName('ows:Identifier')[0].innerHTML))
    tileMatrixSetInfo.ScaleDenominator.push(Number(tileMatrixNode.querySelector('ScaleDenominator').textContent))
    tileMatrixSetInfo.Resolution.push(crsType === 'GEOGCS' ? (Number(tileMatrixNode.querySelector('ScaleDenominator').textContent) * 0.0254) / dpi / 111319.49079327358 : (Number(tileMatrixNode.querySelector('ScaleDenominator').textContent) * 0.0254) / dpi)
    tileMatrixSetInfo.TopLeftCorner.push([parseFloat(lon), parseFloat(lat)]) // 经度在前，纬度在后
    tileMatrixSetInfo.MatrixWidth.push(tileMatrixNode.querySelector('MatrixWidth').textContent)
    tileMatrixSetInfo.MatrixHeight.push(tileMatrixNode.querySelector('MatrixHeight').textContent)
  })
  return tileMatrixSetInfo
}
